//////////////////////////////////////////////////////////////////////////////

//  Thread synchronization facility (Mutex/Lock/Condition)

//////////////////////////////////////////////////////////////////////////////

#ifndef	__MAGUS_THREAD_SYNC_HPP
#define	__MAGUS_THREAD_SYNC_HPP

//////////////////////////////////////////////////////////////////////////////

#include <magus/config.hpp>			// configuration detection

#ifdef __MAGUS_POSIX				// POSIX thread model
#	include <pthread.h>

#elif defined __MAGUS_WIN32		// Win32 thread model
#	include <cassert>
#	include <limits>				// for std::numeric_limits

#	include <windows.h>
#	include <process.h>
#endif

#include <boost/utility.hpp>		// for utility "noncopyable"

//////////////////////////////////////////////////////////////////////////////

namespace magus
{

#ifdef __MAGUS_POSIX		// POSIX thread model


		//// Mutex ///////////////////////////////////////////////////////////////////
		class mutex : private boost::noncopyable
		{
		public:
			friend class condition;

			//// Inner defined lock //////////////////////////////////////////////////////
			class scoped_lock : private boost::noncopyable
			{
			friend class condition;

			public:
				// Constructor & destructor  -----  Lock & unlock
				explicit scoped_lock(mutex& mtx) : _M_mtx(mtx)
				{
					_M_mtx.do_lock();
				}

				~scoped_lock()
				{
					_M_mtx.do_unlock();
				}

			private:
				mutex& _M_mtx;
			};

		public:
			// Constructor & destructor
			mutex(){ pthread_mutex_init(&_M_mtx_t, NULL); }
			~mutex(){ pthread_mutex_destroy(&_M_mtx_t); }

			// Lock & unlock
			void do_lock(){ pthread_mutex_lock(&_M_mtx_t); }
			void do_unlock(){ pthread_mutex_unlock(&_M_mtx_t); }

		private:
			pthread_mutex_t	_M_mtx_t;
		};

		//// Condition ///////////////////////////////////////////////////////////////
		class condition : private boost::noncopyable
		{
		public:
			// Constructor & destructor
			condition(){ pthread_cond_init(&_M_cond, NULL); }
			~condition(){ pthread_cond_destroy(&_M_cond); }

			// Wait
			void wait(mutex::scoped_lock& lock)
			{
				pthread_cond_wait( &_M_cond, &(lock._M_mtx._M_mtx_t) );
			}

			// Wakeup
			void notify_one(){ pthread_cond_signal(&_M_cond); }
			void notify_all(){ pthread_cond_broadcast(&_M_cond); }

		private:
			pthread_cond_t _M_cond;
		};


#elif defined(__MAGUS_WIN32)		// Win32 thread model


		//// Mutex ///////////////////////////////////////////////////////////////////
		class mutex : private boost::noncopyable
		{
		public:
			friend class condition;

			//// Inner defined lock //////////////////////////////////////////////////////
			class scoped_lock : private boost::noncopyable
			{
			friend class condition;

			public:
				// Constructor & destructor  -----  Lock & unlock
				explicit scoped_lock(mutex& mtx) : _M_mtx(mtx)
				{
					_M_mtx.do_lock();
				}

				~scoped_lock()
				{
					_M_mtx.do_unlock();
				}

			private:
				mutex& _M_mtx;
			};

		public:
			// Constructor & destructor
			mutex(){ InitializeCriticalSection(&_M_cs); }
			~mutex(){ DeleteCriticalSection(&_M_cs); }

			// Lock & unlock
			void do_lock(){ EnterCriticalSection(&_M_cs); }
			void do_unlock(){ LeaveCriticalSection(&_M_cs); }

		private:
			// Critical section
			CRITICAL_SECTION _M_cs;
		};

		//// Condition ///////////////////////////////////////////////////////////////
		class condition : private boost::noncopyable
		{
		public:
			// Constructor & destructor
			condition() : _M_waiting(0)
			{
				InitializeCriticalSection(&_M_cs);
				_M_semaphore = CreateSemaphore(NULL, 0, 10000, NULL);

				assert(_M_semaphore!=NULL);
			}

			~condition()
			{
				CloseHandle(_M_semaphore);
				DeleteCriticalSection(&_M_cs);
			}

			// Wait
			void wait(mutex::scoped_lock& lock)
			{
				// unlock first
				lock._M_mtx.do_unlock();

				// adjust data member before waiting
				EnterCriticalSection(&_M_cs);
				++_M_waiting;
				LeaveCriticalSection(&_M_cs);

				// wait for signal
				WaitForSingleObject(_M_semaphore, INFINITE);

				// lock again
				lock._M_mtx.do_lock();
			}

			// Wakeup
			void notify_one()
			{
				EnterCriticalSection(&_M_cs);
				ReleaseSemaphore(_M_semaphore, 1, NULL);
				--_M_waiting;
				LeaveCriticalSection(&_M_cs);
			}

			void notify_all()
			{
				EnterCriticalSection(&_M_cs);
				ReleaseSemaphore(_M_semaphore, _M_waiting, NULL);
				_M_waiting = 0;
				LeaveCriticalSection(&_M_cs);
			}

		private:
			
			HANDLE _M_semaphore;			// Semaphore Objects threads waited on
			long _M_waiting;				// Number of waiting thread
			CRITICAL_SECTION _M_cs;			// Critical section to synchronize access to data member
		};

#endif

}	// namespace magus

//////////////////////////////////////////////////////////////////////////////

#endif		// #ifndef	__MAGUS_THREAD_SYNC_HPP
